using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using Barrage.Global;
using SlimDX;

namespace Barrage.GUI.Elements
{
    /// <summary>
    /// Event that fires off when a GUISlider's value is changed
    /// </summary>
    /// <param name="buttonId">GUIElement id</param>
    /// <param name="value">current value of the slider</param>
    public delegate void GUISliderHandler(string buttonId,float value);

    /// <summary>
    /// Represents a slider value that can be modified via mouse, keyboard or gamepad
    /// </summary>
    public class GUISlider : GUISelectableElement
    {
        const string XML_WIDTH_ATTRIBUTE = "width";
        const string XML_HEIGHT_ATTRIBUTE = "height";

        const string XML_SLIDER_OFFSET_X_ATTRIBUTE = "sliderOffsetX";
        const string XML_SLIDER_OFFSET_Y_ATTRIBUTE = "sliderOffsetY";
        
        const string XML_SLIDER_WIDTH_ATTRIBUTE = "sliderWidth";
        const string XML_SLIDER_HEIGHT_ATTRIBUTE = "sliderHeight";
        
        const string XML_TEXT_ATTRIBUTE = "text";

        /// <summary>
        /// Fires off when the slider is moved
        /// </summary>
        public event GUISliderHandler OnInteraction;

        Vector2 size;
        /// <summary>
        /// Size of the item for mouse intersection checking.
        /// </summary>
        public Vector2 Size { get { return size; } }

        Vector2 sliderOffset;
        /// <summary>
        /// Offset of the slider for mouse intersection checking
        /// </summary>
        public Vector2 SliderOffset { get { return sliderOffset; } }

        float percent = 0;
        /// <summary>
        /// Gets / Sets the slider position, between 0 and 1
        /// </summary>
        public virtual float Percent { get { return percent; } set { percent = Math.Max(0,Math.Min(1,value)); } }

        Vector2 sliderSize;
        /// <summary>
        /// Size of the slider for mouse intersection checking
        /// </summary>
        public Vector2 SliderSize { get { return sliderSize; } }
        
        bool hovering = false;
        /// <summary>
        /// Returns true when the mouse is intersecting with the item
        /// </summary>
        public bool Hovering { get { return hovering; } }

        float sliderSpeed = 0.5f;
        /// <summary>
        /// Slider advance speed in units / second when using keyboard or gamepad
        /// </summary>
        public float SliderSpeed { get { return sliderSpeed; } set { sliderSpeed = value; } }

        string text;
        /// <summary>
        /// Gets / Sets slider text
        /// </summary>
        public virtual string Text { get { return text; } set { text = value; } }

        bool dragging = false;
        /// <summary>
        /// Returns true when the user has pressed the mouse button on the slider and still hasn't released it
        /// </summary>
        public bool Dragging { get { return dragging; } set { dragging = value; } }

        /// <summary>
        /// Default size of the item for mouse intersection checking. Can be overrided
        /// </summary>
        protected virtual Vector2 DefaultSize { get { return new Vector2(100, 100); } }

        /// <summary>
        /// Default size of the slider for mouse intersection checking. Can be overrided
        /// </summary>
        protected virtual Vector2 DefaultSliderSize { get { return new Vector2(50, 50); } }

        /// <summary>
        /// Default offset of the slider for mouse intersection checking. Can be overrided
        /// </summary>
        protected virtual Vector2 DefaultSliderOffset { get { return new Vector2(0, 0); } }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="layout">Layout that will hold this item</param>
        /// <param name="template">Template that will be used to render this item</param>
        /// <param name="xn">Xml Node that holds attribute values of this item</param>
        public GUISlider(GUILayout layout, string template, XmlNode xn)
            : base(layout,template, xn)
        {
            size = GameMath.ParseVector2(XmlHelper.GetXMLAttribute(xn, XML_WIDTH_ATTRIBUTE), XmlHelper.GetXMLAttribute(xn, XML_HEIGHT_ATTRIBUTE), DefaultSize);
            sliderSize = GameMath.ParseVector2(XmlHelper.GetXMLAttribute(xn, XML_SLIDER_WIDTH_ATTRIBUTE), XmlHelper.GetXMLAttribute(xn, XML_SLIDER_HEIGHT_ATTRIBUTE), DefaultSliderSize);
            sliderOffset = GameMath.ParseVector2(XmlHelper.GetXMLAttribute(xn, XML_SLIDER_OFFSET_X_ATTRIBUTE), XmlHelper.GetXMLAttribute(xn, XML_SLIDER_OFFSET_Y_ATTRIBUTE), DefaultSliderOffset);
            text = Language.LanguageManager.Texts[XmlHelper.GetXMLAttribute(xn, XML_TEXT_ATTRIBUTE)];
        }

        /// <summary>
        /// \internal Updates matrices and item logic
        /// </summary>
        protected override void DoUpdate()
        {
            base.DoUpdate();
            hovering = CheckIntersection();
            if (hovering) Layout.SelectedItemIndex = Order;
            CheckSliderClick();
            if (Selected)
            {
                float previousPercent = Percent;
                if (Keyboard.Instance.KeyState(SlimDX.DirectInput.Key.LeftArrow)||Gamepad.AnyButtonStatus(SlimDX.XInput.GamepadButtonFlags.DPadLeft))
                    Percent -= SliderSpeed * Timer.LastIntervalSeconds;
                if (Keyboard.Instance.KeyState(SlimDX.DirectInput.Key.RightArrow) || Gamepad.AnyButtonStatus(SlimDX.XInput.GamepadButtonFlags.DPadRight))
                    Percent += SliderSpeed * Timer.LastIntervalSeconds;

                Percent += Gamepad.SumStickPositions(Stick.LeftStick).X* Timer.LastIntervalSeconds;

                if (Percent != previousPercent)
                    Interact();
                    
            }
        }

        void CheckSliderClick()
        {
            if (dragging)
            {
                CheckSliderIntersection();

                if (Mouse.Instance.ButtonReleased())
                {
                   dragging = false;
                   Interact();
                }
            }
            
            if (Mouse.Instance.ButtonPressed())
                if (CheckSliderIntersection())
                {
                    dragging = true;
                }
            
                
        }

        bool CheckIntersection()
        {
            Vector3 mPos = new Vector3(Mouse.Instance.Position, 0);
            Vector4 tPos = GameMath.VectorAbs(Vector3.Transform(mPos, Matrix.Invert(motion.WorldMatrix)));

            return ((tPos.X <= size.X / 2f) && (tPos.Y <= size.Y / 2f));
        }

        bool CheckSliderIntersection()
        {
            Vector3 mPos = new Vector3(Mouse.Instance.Position, 0);
            Vector4 tPos = Vector3.Transform(mPos, Matrix.Invert(motion.WorldMatrix))-new Vector4(sliderOffset,0,0);
            Vector4 aPos = GameMath.VectorAbs(tPos);

            bool result =  ((aPos.X <= sliderSize.X / 2f) && (aPos.Y <= sliderSize.Y / 2f));
            if (result || dragging)
                percent = Math.Max(0,Math.Min(1,(sliderSize.X / 2f + tPos.X) / sliderSize.X));
            return result;
        }
        
        /// <summary>
        /// \internal Fires OnInteraction event
        /// </summary>
        public override void Interact()
        {
            if (this.OnInteraction != null)
                this.OnInteraction.Invoke(Id,percent);
        }
    }
}
